﻿using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.IO.Pipelines;
using System.Threading.Tasks;

namespace IOP.AspNetCore.Extension.Upload
{
    /// <summary>
    /// 上传扩展
    /// </summary>
    public static class UploadExtension
    {
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static Task UploadFile(this IFormFile file, string filePath)
        {
            try
            {
                using (var newFile = File.Open(filePath, FileMode.Create))
                {
                    Pipe pipe = new Pipe();
                    Stream fs = file.OpenReadStream();
                    var fillPipe = FillPipeAsync(fs, pipe.Writer);
                    var readPipe = ReadPipeAsync(newFile, pipe.Reader);
                    Task.WaitAll(fillPipe, readPipe);
                    return Task.CompletedTask;
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        private const int MINBUFFERSIZE = 512;
        /// <summary>
        /// 填充管道
        /// </summary>
        /// <param name="writer"></param>
        /// <returns></returns>
        private static async Task FillPipeAsync(Stream steam, PipeWriter writer)
        {
            try
            {
                while (true)
                {
                    Memory<byte> memory = writer.GetMemory(MINBUFFERSIZE);
                    int bytesRead = await steam.ReadAsync(memory);
                    writer.Advance(bytesRead);
                    if (bytesRead == 0) break;
                    FlushResult result = await writer.FlushAsync();
                    if (result.IsCompleted) break;
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                writer.Complete();
            }
        }

        /// <summary>
        /// 读取缓冲区数据并写入文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="reader"></param>
        /// <returns></returns>
        private static async Task ReadPipeAsync(FileStream file, PipeReader reader)
        {
            try
            {
                while (true)
                {
                    ReadResult read = await reader.ReadAsync();
                    var buffer = read.Buffer;
                    if (buffer.IsEmpty && read.IsCompleted) break;
                    foreach (var item in buffer)
                    {
                        await file.WriteAsync(item);
                    }
                    reader.AdvanceTo(buffer.End);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                reader.Complete();
            }
        }
    }
}
